package cn.aoyu.test;

import cn.aoyu.test.util.BaseConcurrentRequestUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * @ClassName: RedisTestDemo
 * @Auther: xww
 * @Date: 2019/5/23 0023 16:18
 * @Description:
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class SpringRedisTestDemo extends BaseConcurrentRequestUtil {

	// applicationContext 解析
	private static ApplicationContext context = new ClassPathXmlApplicationContext("spring-redis.xml");

	private static RedisTemplate redisTemplate = (RedisTemplate) context.getBean("redisTemplate");

	/**
	 * redis事务
	 *
	 * @throws InterruptedException
	 */
	@Test
	public void redisTest() throws InterruptedException {
		// 总的请求个数
		int requestTotal = 1;
		// 同一时刻最大的并发线程的个数
		int concurrentThreadNum = 1;
		this.execute(requestTotal, concurrentThreadNum);
	}

	/**
	 * 需要执行并发的方法
	 */
	@Override
	public void executeMethod() {
		multiAndWatch();
	}

	//    @Transactional
	//    哪怕加了这个注解spring的配置文件里redistemplate配置也要开启事务支持
	private void multiAndWatch() {
		ValueOperations<String, String> vo = redisTemplate.opsForValue();
		redisTemplate.setEnableTransactionSupport(true);

		// Redis::multi()将多个操作当做一个事务来处理。
		redisTemplate.multi();
		vo.set("b", "1");
		vo.get("b");
		// Redis::discard()函数取消一个事物处理模式。
		redisTemplate.discard();

		// 原子性: multi()返回一个Redis实例，并且这个实例进入到了事务处理模式（批量处理）。
		// 当进入到事务处理模式，所有的方法调用都将返回相同的Redis实例，一直到exec()被调用执行事务处理。
		redisTemplate.multi();
		vo.set("a", "1");
		vo.set("b", "2");
		vo.get("a");
		redisTemplate.exec();

		redisTemplate.setEnableTransactionSupport(false);
		// 乐观锁: watch() 监控一个KEY是否被其他的客户端修改。如果KEY在调用watch()和exec()之间被修改，那么批量处理最终的exec()执行将失败。
		// 通过一些实验，这个函数的效果其实并没有那么好，或者说不能够准确的去监控。
		List<Object> rs = null;
		do {
			redisTemplate.watch("a");
			redisTemplate.multi();
			vo.set("a", "2");
			rs = redisTemplate.exec();
		} while (rs == null);//多重检测，直到执行成功。

		System.out.println("a2:" + vo.get("a"));
		System.out.println("b2:" + vo.get("b"));
	}

	/**
	 * 清理redis中的数据
	 */
	private void flushdb() {
		redisTemplate.execute(new RedisCallback<Object>() {
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				connection.flushDb();
				return "ok";
			}
		});
	}

	/**
	 * 切换数据库
	 *
	 * @param index 数据库的序号
	 */
	private void cutRedisDB(int index) {
		try {
			RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
			DefaultStringRedisConnection stringRedisConnection = new DefaultStringRedisConnection(connection);
			stringRedisConnection.select(index);  // index = 2
			stringRedisConnection.set("test", "test11");
		} catch (Exception e) {

		}
	}

}
